package cn.csc.module.product.service.spu;

import cn.csc.framework.common.pojo.CommonResult;
import cn.csc.framework.common.pojo.PageResult;
import cn.csc.framework.common.util.number.ObjectNumberUtils;
import cn.csc.framework.common.util.validation.ValidationUtils;
import cn.csc.module.product.api.spu.dto.SpuDTO;
import cn.csc.module.product.controller.admin.spu.vo.*;
import cn.csc.module.product.controller.app.spu.vo.AppSpuCreateReqVO;
import cn.csc.module.product.convert.attr.AttrConvert;
import cn.csc.module.product.convert.attrvalue.AttrValueConvert;
import cn.csc.module.product.convert.sku.SkuConvert;
import cn.csc.module.product.convert.spu.SpuConvert;
import cn.csc.module.product.dal.dataobject.attr.AttrDO;
import cn.csc.module.product.dal.dataobject.attrjson.AttrJsonDO;
import cn.csc.module.product.dal.dataobject.attrvalue.AttrValueDO;
import cn.csc.module.product.dal.dataobject.sku.SkuDO;
import cn.csc.module.product.dal.dataobject.skuattrjson.SkuAttrJsonDO;
import cn.csc.module.product.dal.dataobject.spu.SpuDO;
import cn.csc.module.product.dal.dataobject.spudetail.SpuDetailDO;
import cn.csc.module.product.dal.dataobject.tagref.SpuTagReferenceDO;
import cn.csc.module.product.dal.mysql.attr.AttrMapper;
import cn.csc.module.product.dal.mysql.attrjson.AttrJsonMapper;
import cn.csc.module.product.dal.mysql.attrvalue.AttrValueMapper;
import cn.csc.module.product.dal.mysql.category.CategoryMapper;
import cn.csc.module.product.dal.mysql.sku.SkuMapper;
import cn.csc.module.product.dal.mysql.skuattrjson.SkuAttrJsonMapper;
import cn.csc.module.product.dal.mysql.spu.SpuMapper;
import cn.csc.module.product.dal.mysql.spudetail.SpuDetailMapper;
import cn.csc.module.product.dal.mysql.tagref.SpuTagReferenceMapper;
import cn.csc.module.product.validated.Verify1;
import cn.csc.module.shop.api.detail.ShopDetailApi;
import cn.csc.module.shop.api.detail.dto.ShopDetailRespDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.math.BigInteger;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import static cn.csc.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.csc.module.product.enums.ErrorCodeConstants.*;

/**
 * spu信息 Service 实现类
 *
 * @author liuchuang
 */
@Slf4j
@Service
@Validated
public class SpuServiceImpl implements SpuService {

    @Resource
    private SpuMapper spuMapper;
    @Resource
    private AttrJsonMapper attrJsonMapper;
    @Resource
    private SkuAttrJsonMapper skuAttrJsonMapper;
    @Resource
    private SpuDetailMapper spuDetailMapper;
    @Resource
    private SkuMapper skuMapper;
    @Resource
    private CategoryMapper categoryMapper;

    @Resource
    private AttrMapper attrMapper;

    @Resource
    private AttrValueMapper attrValueMapper;

    @Resource
    private SpuTagReferenceMapper spuTagReferenceMapper;

    @Resource
    ShopDetailApi shopDetailApi;


    @Transactional
    @Override
    public BigInteger createSpu(SpuCreateDataReqVO createReqVO) {

        CommonResult<ShopDetailRespDTO> result = shopDetailApi.getShopDetail(createReqVO.getShopId());
        if (result.getCode() == 0) {
            ShopDetailRespDTO dto = result.getData();
            if (dto == null) {
                throw exception(SPU_SHOPNAME_NOT_EXISTS);
            }
            createReqVO.setShopName(dto.getShopName());
        } else {
            throw exception(SPU_SHOPNAME_ERROR);
        }
        // 插入
        SpuDO spu = SpuConvert.INSTANCE.convert(createReqVO);
        spu.setMarketPriceFee(createReqVO.getPriceFee());
        if (spu.getVerifyStatus() != null && spu.getVerifyStatus() == 5)
            spu.setOnlineTime(LocalDateTime.now());

        spuMapper.insert(spu);
        SpuDetailDO detailDO = new SpuDetailDO();
        detailDO.setId(spu.getId());
        detailDO.setDetail(createReqVO.getDetail());
        detailDO.setDetailImg(createReqVO.getDetailImg());
        spuDetailMapper.insert(detailDO);

        Optional.ofNullable(createReqVO.getSkuAttr()).ifPresent(s -> {
            long count = s.stream().map(t -> t.getAttrName()).distinct().count();
            if (count < s.size()) {
                throw exception(ATTR_JSON_EXISTS);
            }

            String attrValues = s.stream().map(t -> t.getAttrName()).collect(Collectors.joining(","));
            AttrJsonDO jsonDO = new AttrJsonDO();
            jsonDO.setAttrName("规格");
            jsonDO.setAttrValue(attrValues);
            jsonDO.setSpuId(spu.getId());
            attrJsonMapper.insert(jsonDO);
            s.forEach(t -> {
                ValidationUtils.validate(t);
                String code = ObjectNumberUtils.getOrder("");
                t.setPartyCode(code);
                SkuDO skuDO = new SkuDO();
                skuDO.setSpuId(spu.getId());
                skuDO.setPartyCode(ObjectNumberUtils.getOrder(""));
                skuDO.setPriceFee(t.getPriceFee());
                skuDO.setShopPriceFee(t.getShopPriceFee());
                skuMapper.insert(skuDO);
                t.setSkuId(skuDO.getId());
            });

            SkuAttrJsonDO skuAttrJsonDO = new SkuAttrJsonDO();
            skuAttrJsonDO.setSpuId(spu.getId());
            skuAttrJsonDO.setAttrJson(createReqVO.getSkuAttr());
            skuAttrJsonMapper.insert(skuAttrJsonDO);
        });

        return spu.getId();
    }

    //还是怕有重复的信息，目前暂不使用
    private String handleSkuCode(int index, BigInteger productId) {

        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
        StringBuilder sb = new StringBuilder();
        //日期
        sb.append(sdf.format(new Date()));
        //四位商品id
        sb.append(String.format("%04d", productId));
        //三位索引id
        sb.append(String.format("%03d", index + 1));
        return sb.toString();
    }

    @Transactional
    @Override
    public void updateSpu(SpuCreateDataReqVO createReqVO) {
        // 校验存在
        validateSpuVerifyStatus(createReqVO.getId());

        int count = categoryMapper.selectCountById(createReqVO.getCategoryId());
        if (count <= 0)
            throw exception(CATEGORY_NOT_EXISTS);

        // 更新
        SpuDO spu = SpuConvert.INSTANCE.convert(createReqVO);
        spu.setMarketPriceFee(spu.getPriceFee());
        spuMapper.updateById(spu);

        SpuDetailDO detailDO = new SpuDetailDO();
        detailDO.setId(spu.getId());
        detailDO.setDetail(createReqVO.getDetail());
        spuDetailMapper.updateById(detailDO);

        if (createReqVO.getSkuAttr() != null) {
            String attrValues = createReqVO.getSkuAttr().stream().map(s -> s.getAttrName()).collect(Collectors.joining(","));
            AttrJsonDO jsonDO = new AttrJsonDO();
            jsonDO.setAttrValue(attrValues);
            AttrJsonDO attrJsonDO1 = attrJsonMapper.selectOneDO(spu.getId());
            if (attrJsonDO1 == null) {
                jsonDO.setAttrName("规格");
                jsonDO.setSpuId(spu.getId());
                attrJsonMapper.insert(jsonDO);
            } else {
                attrJsonMapper.updateAttrJson(spu.getId(), jsonDO);
            }

            //先查询出原来有的sku
            List<SkuDO> list = skuMapper.selectList(spu.getId());
            Set<BigInteger> set = new HashSet<>();
            if (list != null) {
                set = list.stream().map(s -> s.getId()).collect(Collectors.toList()).stream().collect(Collectors.toSet());
                List<SkuDO> lt = list.stream()
                        .filter(a -> createReqVO.getSkuAttr().stream().noneMatch(b -> a.getId().equals(b.getSkuId())))
                        .collect(Collectors.toList());
                lt.stream().forEach(s -> {
                    skuMapper.deleteById(s);
                });
            }

            Set<BigInteger> finalSet = set;
            createReqVO.getSkuAttr().forEach(s -> {
                ValidationUtils.validate(s);
                SkuDO skuDO = new SkuDO();
                if (!finalSet.contains(s.getSkuId())) {
                    String code = ObjectNumberUtils.getOrder("");
                    s.setPartyCode(code);
                    skuDO.setSpuId(spu.getId());
                    skuDO.setPartyCode(ObjectNumberUtils.getOrder(""));
                    skuDO.setPriceFee(s.getPriceFee());
                    skuMapper.insert(skuDO);
                    s.setSkuId(skuDO.getId());
                } else {
                    skuDO.setPriceFee(s.getPriceFee());
                    skuMapper.updateSku(skuDO, s.getSkuId());
                }
            });

            SkuAttrJsonDO skuJsonDO = skuAttrJsonMapper.selectOneDO(spu.getId());
            SkuAttrJsonDO skuAttrJsonDO = new SkuAttrJsonDO();
            skuAttrJsonDO.setAttrJson(createReqVO.getSkuAttr());
            if (skuJsonDO == null) {
                skuAttrJsonDO.setSpuId(spu.getId());
                skuAttrJsonMapper.insert(skuAttrJsonDO);
            } else {
                skuAttrJsonMapper.updateSkuAttrJson(spu.getId(), skuAttrJsonDO);
            }
        } else {
            skuMapper.delBySpuId(spu.getId());
            attrJsonMapper.delAttrJson(spu.getId());
            skuAttrJsonMapper.delSkuAttrJson(spu.getId());
        }
    }

    @Override
    public void verifySpu(SpuUpdateReqVO updateReqVO) {
        SpuDO spuDO = SpuConvert.INSTANCE.convert(updateReqVO);

        spuDO.setVerifyTime(LocalDateTime.now());
        if (spuDO.getVerifyStatus() == 10)
            spuDO.setStatus(1);
        else if (spuDO.getVerifyStatus() == 15)
            spuDO.setStatus(0);

        spuMapper.updateById(spuDO);
    }

    @Transactional
    @Override
    public void addShopSpuBatch(List<SpuShopDataReqVO> list) {
        list.stream().forEach(s -> {
            addShopSpu(s);
        });
    }

    @Transactional
    @Override
    public void addShopSpu(SpuShopDataReqVO spuShopDataReqVO) {
        // 校验存在
        validateSpuExists(spuShopDataReqVO.getId());

        SpuDO spu = SpuConvert.INSTANCE.convert(spuShopDataReqVO);
        spu.setShopMarketPriceFee(spuShopDataReqVO.getShopPriceFee());
        spu.setHasShop(1);

        spuShopDataReqVO.getSkuAttr().stream().forEach(s -> {
            s.setHasShop(1);
            ValidationUtils.validate(s, Verify1.class);
            SkuDO skuDO = new SkuDO();
            skuDO.setShopPriceFee(s.getShopPriceFee());
            skuMapper.updateSku(skuDO, s.getSkuId());
        });
        // 更新
        spuMapper.updateById(spu);

        List<SkuDO> list = skuMapper.selectList(spu.getId());
        if (list.size() != spuShopDataReqVO.getSkuAttr().size()) {
            throw exception(SPU_SIZE_VERIFY);
        }

        SkuAttrJsonDO skuAttrJsonDO = new SkuAttrJsonDO();
        skuAttrJsonDO.setAttrJson(spuShopDataReqVO.getSkuAttr());
        skuAttrJsonMapper.updateSkuAttrJson(spu.getId(), skuAttrJsonDO);
    }

    @Transactional
    @Override
    public void updateShelf(SpuUpdateReqVO updateReqVO) {
        SpuDO updateObj = SpuConvert.INSTANCE.convert(updateReqVO);
        SpuDO spu = getSpu(updateReqVO.getId());
        if (updateReqVO.getVerifyStatus() == 10) {
            if (spu.getVerifyStatus() != 5) {
                throw exception(SPU_STATUS_VERIFY2);
            }

            int count = categoryMapper.selectCountById(spu.getCategoryId());
            if (count <= 0)
                throw exception(CATEGORY_NOT_EXISTS);

            updateObj.setStatus(1);
            updateObj.setVerifyTime(LocalDateTime.now());
        } else if (updateReqVO.getVerifyStatus() == 15) {
            if (spu.getVerifyStatus() != 5 && spu.getVerifyStatus() != 10) {
                throw exception(SPU_STATUS_VERIFY2);
            }
            updateObj.setStatus(0);
        } else {
            throw exception(SPU_STATUS_VERIFY2);
        }

        List<String> categoryIds = Arrays.asList(spu.getCategoryLevel().split(","));
        spuMapper.updateById(updateObj);
        String num = "";
        if (updateReqVO.getVerifyStatus() == 10 && spu.getVerifyStatus() == 5) {
            num = "+1";
            categoryMapper.updateProductNum(categoryIds, num);
        } else if (updateReqVO.getVerifyStatus() == 15 && spu.getVerifyStatus() == 10) {
            num = "-1";
            categoryMapper.updateProductNum(categoryIds, num);
        }
    }

    @Override
    public void onShelf(SpuUpdateReqVO updateReqVO) {
        SpuDO updateObj = SpuConvert.INSTANCE.convert(updateReqVO);
        SpuDO spu = getSpu(updateReqVO.getId());
        if (updateReqVO.getVerifyStatus() == 5) {
            if (spu.getVerifyStatus() != 0 && spu.getVerifyStatus() != 15) {
                throw exception(SPU_STATUS_VERIFY2);
            }
            int count = categoryMapper.selectCountById(spu.getCategoryId());
            if (count <= 0)
                throw exception(CATEGORY_NOT_EXISTS);

            updateObj.setStatus(0);
            updateObj.setOnlineTime(LocalDateTime.now());
        } else if (updateReqVO.getVerifyStatus() == 0) {
            updateObj.setStatus(0);
        } else {
            throw exception(SPU_STATUS_VERIFY2);
        }
        spuMapper.updateById(updateObj);

        List<String> categoryIds = Arrays.asList(spu.getCategoryLevel().split(","));
        if (updateReqVO.getVerifyStatus() == 0 && spu.getVerifyStatus() == 10) {
            String num = "-1";
            categoryMapper.updateProductNum(categoryIds, num);
        }
    }

    @Transactional
    @Override
    public void deleteSpu(BigInteger id) {
        // 校验存在
        validateSpuVerifyStatus(id);
        // 删除
        spuMapper.deleteById(id);
        spuDetailMapper.deleteById(id);
        attrJsonMapper.delAttrJson(id);
        skuAttrJsonMapper.delSkuAttrJson(id);
    }

    private void validateSpuExists(BigInteger id) {
        if (spuMapper.selectById(id) == null) {
            throw exception(SPU_NOT_EXISTS);
        }
    }

    private void validateSpuVerifyStatus(BigInteger id) {
        SpuDO spuDO = spuMapper.selectById(id);
        if (spuDO == null) {
            throw exception(SPU_NOT_EXISTS);
        }
        if (spuDO.getVerifyStatus() != 0 && spuDO.getVerifyStatus() != 15)
            throw exception(SPU_STATUS_VERIFY);
    }

    @Override
    public SpuDO getSpu(BigInteger id) {
        return spuMapper.getSpu(id);
    }

    @Override
    public List<SpuDO> getSpuList(Collection<BigInteger> ids) {
        return spuMapper.selectBatchIds(ids);
    }

    @Override
    public PageResult<SpuDO> getSpuPage(SpuPageReqVO pageReqVO) {
        return spuMapper.selectPage(pageReqVO);
    }

    @Override
    public PageResult<SpuDO> selectPageByOnLineTimeDesc(SpuPageReqVO pageReqVO) {
        List<String> verifyStatusList = new ArrayList<>();
        if (pageReqVO.getVerifyStatus() != null) {
            verifyStatusList.add(String.valueOf(pageReqVO.getVerifyStatus()));
            if (pageReqVO.getVerifyStatus() == 0)
                verifyStatusList.add("15");
        }
        pageReqVO.setStatusList(verifyStatusList);
        return spuMapper.selectPageByOnLineTimeDesc(pageReqVO);
    }

    @Override
    public PageResult<SpuDO> getSpuPageSingle(SpuPageReqVO pageReqVO) {
        return spuMapper.selectPageSingle(pageReqVO);
    }

    @Override
    public List<SpuDO> getSpuList(SpuExportReqVO exportReqVO) {
        return spuMapper.selectList(exportReqVO);
    }

    @Override
    public Long selectOKSpuNum(BigInteger shopId) {
        SpuDTO spuDTO = new SpuDTO();
        spuDTO.setShopId(shopId);
        spuDTO.setVerifyStatus(10);
        return spuMapper.selectOKSpuNum(spuDTO);
    }

    @Override
    public SpuDataRespVO getSpuById(BigInteger id) {
        SpuDO spuDO = getSpu(id);
        if(spuDO == null)
            throw exception(SPU_NOT_EXISTS);

        SpuDataRespVO respVO = SpuConvert.INSTANCE.convertData(spuDO);

        SpuDetailDO detailDO = spuDetailMapper.selectById(id);
        if(detailDO != null) {
            respVO.setDetail(detailDO.getDetail());
            respVO.setDetailImg(detailDO.getDetailImg());
        }

        AttrJsonDO attrJsonDO = attrJsonMapper.selectOneDO(id);
        Optional.ofNullable(attrJsonDO).ifPresent(s -> respVO.setAttrValue(attrJsonDO.getAttrValue()));

        SkuAttrJsonDO skuAttrJsonDO = skuAttrJsonMapper.selectOneDO(id);
        Optional.ofNullable(skuAttrJsonDO).ifPresent(s -> respVO.setSkuAttr(skuAttrJsonDO.getAttrJson()));
        return respVO;
    }

    /**
     * 根据店铺id列表查询各店铺审核通过的商品有多少个
     *
     * @param list 店铺id列表
     * @return
     */
    @Override
    public Map<String, Object> getSkuCountByShopIds(List<BigInteger> list) {
        List<Map<String, Object>> mapList = spuMapper.selectSkuCountByShopIds(10, list);
        Map<String, Object> collect = mapList.stream()
                .collect(Collectors.toMap(map -> map.get("shopId").toString(), map -> map.get("count"), (m1, m2) -> m2));
        return collect;
    }

    /**
     * 选品平台创建商品
     * @param reqVO 参数
     * @return
     */
    @Transactional
    @Override
    public BigInteger appCreateSpuV2(AppSpuCreateReqVO reqVO){
        // 保存spu
        SpuDO spuDO = SpuConvert.INSTANCE.convert(reqVO);
        spuDO.setType(20);  // 商品类型(10普通，20选品)
        spuMapper.insert(spuDO);

        // 保存基本属性
        reqVO.getBasicAttrs().forEach(a -> a.setShopId(reqVO.getShopId()));
        List<AttrDO> basicAttrDOS = AttrConvert.INSTANCE.convertList03(reqVO.getBasicAttrs());
        attrMapper.insertBatch(basicAttrDOS);
        // 保存基本属性的值
        IntStream.range(0,basicAttrDOS.size()).forEach(i -> reqVO.getBasicAttrs().get(i).setId(basicAttrDOS.get(i).getId()));
        List<AttrValueDO> attrValueDOS = AttrValueConvert.INSTANCE.convertList03(reqVO.getBasicAttrs());
        attrValueMapper.insertBatch(attrValueDOS);

        // 保存销售属性
        reqVO.getSalesAttrs().forEach(a -> a.setShopId(reqVO.getShopId()));
        List<AttrDO> salesAttrDOS = AttrConvert.INSTANCE.convertList03(reqVO.getSalesAttrs());
        attrMapper.insertBatch(salesAttrDOS);

        // 保存sku list
        List<SkuDO> skuDOS = SkuConvert.INSTANCE.convertList03(reqVO.getSkuList());
        skuDOS.forEach(sku -> sku.setSpuId(spuDO.getId()));
        skuMapper.insertBatch(skuDOS);

        // 保存商品标签
        List<SpuTagReferenceDO> tagReferenceDOList = reqVO.getTagIdList().stream().map(tagId -> SpuTagReferenceDO.builder().shopId(reqVO.getShopId()).spuId(spuDO.getId())
                .tagId(tagId).status(true)
                .build()).collect(Collectors.toList());
        spuTagReferenceMapper.insertBatch(tagReferenceDOList);

        // 保存商品详情
        SpuDetailDO spuDetailDO = SpuDetailDO.builder().id(spuDO.getId()).detail(reqVO.getDetail()).build();
        spuDetailMapper.insert(spuDetailDO);

        // todo 创建审核记录

        return spuDO.getId();
    }


}
